package yao.config;

import java.io.File;
import java.io.FilenameFilter;
import java.util.List;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import yao.config.exception.ConfigException;
import yao.config.exception.ConfigPathException;
import yao.config.exception.ConfigSettingException;


/**
 * XML配置文件读取类
 * 
 * @author YaoMing
 */
final class XmlReader {

	/** 路径分析器 */
	ConfigPath configPath;

	Storage storage;

	Set<File> files;

	XmlReader(String path) throws ConfigException {
		try {
			configPath = new ConfigPath(path);
		} catch (ConfigPathException e) {
			throw new ConfigException(e);
		}
	}

	/**
	 * 读XML配制文档
	 * 
	 * @param path
	 * @throws ConfigException
	 * @throws ConfigPathException
	 */
	void read() throws ConfigException, ConfigPathException {
		if (files.contains(configPath.getFile())) {
			return;
		}

		files.add(configPath.getFile());
		SAXReader sax = new SAXReader();
		Document dom = null;
		try {
			dom = sax.read(configPath.getFile());
		} catch (DocumentException e) {
			throw new ConfigException(e, "Read config file \"" + configPath.getPath() + "\" error.");
		}

		Element rootE = dom.getRootElement();
		List<?> listE = rootE.elements(PathContants.NODE);
		this.loadNodes(null, listE);

		listE = rootE.elements(PathContants.IMPORT_FILE);
		if (null != listE) {
			loadFile(listE);
		}
		listE = rootE.elements(PathContants.IMPORT_DIR);
		if (null != listE) {
			loadDir(listE);
		}
	}

	/**
	 * 加载配置文件目录
	 * 
	 * @param listE
	 * @throws ConfigException
	 * @throws ConfigPathException
	 */
	private void loadDir(List<?> listE) throws ConfigException, ConfigPathException {
		for (Object o : listE) {
			Element e = (Element) o;
			String path = e.attributeValue(PathContants.PATH);
			ConfigPath pathParser;
			try {
				pathParser = new ConfigPath(path);
			} catch (ConfigPathException e1) {
				throw new ConfigException(e1, "Load dir error, by element \"import-dir\", from \"" + this.configPath.getPath() + "\"");
			}
			if (!pathParser.getFile().isDirectory()) {
				throw new ConfigPathException("\"" + path + "\" is not a directory, by element \"import-dir\", from \"" + pathParser.getPath() + "\"");
			} else {
				final String filename_pattern = e.attributeValue(PathContants.FILENAME_PATTERN);
				File[] files = pathParser.getFile().listFiles(new FilenameFilter() {
					public boolean accept(File dir, String name) {
						if (filename_pattern != null && name.matches(filename_pattern))
							return true;
						return false;
					}
				});
				if (null == files) {
					return;
				}
				for (File file : files) {
					XmlReader xmlReader = new XmlReader(file.getAbsolutePath());
					xmlReader.files = this.files;
					xmlReader.storage = storage;
					xmlReader.read();
				}
			}
		}
	}

	/**
	 * 加载配置文件
	 * 
	 * @param listE
	 * @throws ConfigException
	 * @throws ConfigPathException
	 */
	private void loadFile(List<?> listE) throws ConfigException, ConfigPathException {
		for (Object o : listE) {
			Element e = (Element) o;
			String path = e.attributeValue(PathContants.PATH);
			if (null != path && !path.trim().equals("")) {
				XmlReader xmlReader = new XmlReader(path);
				xmlReader.files = files;
				xmlReader.storage = storage;
				xmlReader.read();
			}
		}
	}

	/**
	 * 从ElementList中load Node并设置到节点中
	 * 
	 * @param parent
	 * @param listE
	 * @throws ConfigException
	 */
	private void loadNodes(Node parent, List<?> listE) throws ConfigException {
		if (listE == null || listE.size() == 0) {
			return;
		}
		for (Object o : listE) {
			Element e = (Element) o;
			// 从Element中取Node对象
			ENode eNode;
			try {
				eNode = Element2Node.element2Node(e);
			} catch (ConfigSettingException ex) {
				throw new ConfigException(ex, configPath.getPath());
			}

			this.loadNodes(mergeNode(eNode, parent), e.elements(PathContants.NODE));
		}
	}

	/**
	 * 合并节点
	 * 
	 * @param map
	 * @param eNode
	 * @return
	 * @throws ConfigException
	 */
	private Node mergeNode(ENode eNode, Node parent) throws ConfigException {
		// 集合中拿到已经存在的节点
		String fullname = "";
		if (null == parent) {
			fullname = eNode.getName();
		} else {
			fullname = parent.getFullName() + "." + eNode.getName();
		}

		Node node = storage.nodeMap.get(fullname);

		// 如果节点为空
		if (null == node) {
			// 将新的直接放进去
			node = new Node();
			storage.nodeMap.put(fullname, node);
			node.name = eNode.getName();// 名称
			node.fullName = fullname;// 全名
		} else {
			// 是否被两次赋值
			if (null != node.getValue().toString() && (null != eNode.getAttributeValue() || null != eNode.getTextValue())) {
				throw new ConfigException("Can not set value to more than two nodes in same name, in \"" + node.getFullName() + "\" from \"" + configPath.getPath() + "\"");
			}

			// 判断是不是有同一路径的两个"可写"项为真
			if (node.isWriteable() && eNode.isWriteable()) {
				throw new ConfigException("Cat not set writeable(true) to more than two nodes in same name, in \"" + node.getFullName() + "\" from \"" + configPath.getPath() + "\"");
			}

			// 判断是不是已经有值了(如果节点已经有值了,就不能再设置成可变了)
			if (null != node.getValue().toString() && eNode.isWriteable()) {
				throw new ConfigException("Cat not set writeable(true) to \"" + node.getFullName() + "\", because the node had bean setted value, in \"" + configPath.getPath() + "\"");
			}

			// 判断是不是已经被设置了"可写"项(如果节点已经设置成可变了,就不能再赋值)
			if (node.isWriteable() && (null != eNode.getAttributeValue() || null != eNode.getTextValue())) {
				throw new ConfigException("Cat not set value to \"" + node.getFullName() + "\", because the node had bean setted writeable(true), in \"" + configPath.getPath() + "\"");
			}
		}

		// 值
		if (null == node.value || null == node.value.toString()) {
			if (null != eNode.getAttributeValue()) {
				node.value = new Value(eNode.getAttributeValue());
			} else {
				node.value = new Value(eNode.getTextValue());
			}
		}

		// 是否可写
		node.writeable |= (eNode.isWriteable());

		// 设置有值的那个节点的TEXT
		if (null == node.getText() || (null != eNode.getAttributeValue() || null != eNode.getTextValue())) {
			node.text = (eNode.getText());
		}

		if (node.configPath.size() != 0) {
			ConfigPath _configPath = node.configPath.get(node.configPath.size() - 1);
			if (!configPath.equals(_configPath)) { // 判断是否同一个文件
				node.configPath.add(configPath);
			}
		} else {
			node.configPath.add(configPath);
		}

		if (null != parent) {
			parent.nodeMap.put(node.name, node);
		}

		try {
			node.valueMap = Element2ValueMap.element2ValueMap(eNode.valueMapElement);
		} catch (ConfigSettingException e) {
			throw new ConfigException(e, "In ");
		}

		return node;
	}
}
